"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.http = exports.addUserAgentHeader = void 0;
const path_1 = __importDefault(require("path"));
const fs_extra_1 = __importDefault(require("fs-extra"));
const content_disposition_1 = __importDefault(require("content-disposition"));
const config_1 = require("../config");
const getAxiosConfig_1 = require("./getAxiosConfig");
const addQueryParams_1 = require("./addQueryParams");
const personalAccessKey_1 = require("../lib/personalAccessKey");
const oauth_1 = require("../lib/oauth");
const logger_1 = require("../lib/logger");
const lang_1 = require("../utils/lang");
const auth_1 = require("../constants/auth");
const client_1 = require("./client");
const i18nKey = 'http.index';
function addUserAgentHeader(key, value) {
    getAxiosConfig_1.USER_AGENTS[key] = value;
}
exports.addUserAgentHeader = addUserAgentHeader;
async function withOauth(account, axiosConfig) {
    const { headers } = axiosConfig;
    const oauth = (0, oauth_1.getOauthManager)(account);
    if (!oauth) {
        throw new Error((0, lang_1.i18n)(`${i18nKey}.errors.withOauth`, { accountId: account.accountId }));
    }
    const accessToken = await oauth.accessToken();
    return {
        ...axiosConfig,
        headers: {
            ...headers,
            Authorization: `Bearer ${accessToken}`,
        },
    };
}
async function withPersonalAccessKey(accountId, axiosConfig) {
    const { headers } = axiosConfig;
    const accessToken = await (0, personalAccessKey_1.accessTokenForPersonalAccessKey)(accountId);
    return {
        ...axiosConfig,
        headers: {
            ...headers,
            Authorization: `Bearer ${accessToken}`,
        },
    };
}
function withPortalId(portalId, axiosConfig) {
    const { params } = axiosConfig;
    return {
        ...axiosConfig,
        params: {
            ...params,
            portalId,
        },
    };
}
async function withAuth(accountId, options) {
    const account = (0, config_1.getConfigAccountById)(accountId);
    const { env, authType } = account;
    const axiosConfig = withPortalId(accountId, (0, getAxiosConfig_1.getAxiosConfig)({ env, ...options }));
    if (authType === auth_1.PERSONAL_ACCESS_KEY_AUTH_METHOD.value) {
        return withPersonalAccessKey(accountId, axiosConfig);
    }
    if (authType === auth_1.OAUTH_AUTH_METHOD.value) {
        return withOauth(account, axiosConfig);
    }
    if (authType === auth_1.API_KEY_AUTH_METHOD.value) {
        const { params } = axiosConfig;
        return {
            ...axiosConfig,
            params: {
                ...params,
                hapikey: account.apiKey,
            },
        };
    }
    throw new Error((0, lang_1.i18n)(`${i18nKey}.errors.invalidAuthType`, {
        accountId,
        authType,
    }));
}
async function getRequest(accountId, options) {
    const { params, ...rest } = options;
    const optionsWithParams = (0, addQueryParams_1.addQueryParams)(rest, params);
    const requestConfig = await withAuth(accountId, optionsWithParams);
    return (0, client_1.httpClient)(requestConfig);
}
async function postRequest(accountId, options) {
    const requestConfig = await withAuth(accountId, options);
    return (0, client_1.httpClient)({ ...requestConfig, method: 'post' });
}
async function putRequest(accountId, options) {
    const requestConfig = await withAuth(accountId, options);
    return (0, client_1.httpClient)({ ...requestConfig, method: 'put' });
}
async function patchRequest(accountId, options) {
    const requestConfig = await withAuth(accountId, options);
    return (0, client_1.httpClient)({ ...requestConfig, method: 'patch' });
}
async function deleteRequest(accountId, options) {
    const requestConfig = await withAuth(accountId, options);
    return (0, client_1.httpClient)({ ...requestConfig, method: 'delete' });
}
function createGetRequestStream(contentType) {
    return async (accountId, options, destPath) => {
        const { params, ...rest } = options;
        const axiosConfig = (0, addQueryParams_1.addQueryParams)(rest, params);
        // eslint-disable-next-line no-async-promise-executor
        return new Promise(async (resolve, reject) => {
            try {
                const { headers, ...opts } = await withAuth(accountId, axiosConfig);
                const res = await (0, client_1.httpClient)({
                    method: 'get',
                    ...opts,
                    headers: {
                        ...headers,
                        accept: contentType,
                    },
                    responseType: 'stream',
                });
                if (res.status >= 200 && res.status < 300) {
                    let filepath = destPath;
                    if (fs_extra_1.default.existsSync(destPath)) {
                        const stat = fs_extra_1.default.statSync(destPath);
                        if (stat.isDirectory()) {
                            const { parameters } = content_disposition_1.default.parse(res.headers['content-disposition'] || '');
                            filepath = path_1.default.join(destPath, parameters.filename);
                        }
                    }
                    try {
                        fs_extra_1.default.ensureFileSync(filepath);
                    }
                    catch (err) {
                        reject(err);
                    }
                    const writeStream = fs_extra_1.default.createWriteStream(filepath, {
                        encoding: 'binary',
                    });
                    res.data.pipe(writeStream);
                    writeStream.on('error', err => {
                        reject(err);
                    });
                    writeStream.on('close', async () => {
                        logger_1.logger.log((0, lang_1.i18n)(`${i18nKey}.createGetRequestStream.onWrite`, {
                            filepath,
                        }));
                        resolve(res);
                    });
                }
                else {
                    reject(res);
                }
            }
            catch (err) {
                reject(err);
            }
        });
    };
}
const getOctetStream = createGetRequestStream('application/octet-stream');
exports.http = {
    get: getRequest,
    post: postRequest,
    put: putRequest,
    patch: patchRequest,
    delete: deleteRequest,
    getOctetStream,
};
